﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace FVEcharts
{
    /// <summary>
    /// FVEcharts.xaml 的交互逻辑
    /// </summary>
    public partial class Echart : UserControl
    {
        private DpiScale _dpiInfo;
        private readonly object _sync = new object();
        private readonly double _defaultBarWidth = 30;
        private readonly double _minSpaceWidth = 6;
        private readonly double _minBarWidth = 6;
        private readonly double _legendHeight = 30;
        private readonly double _defaultFontSize = 12;
        private readonly string _defaultFontName = "Microsoft YaHei";
        private List<LegendItem> _legendItems = new List<LegendItem>();

        /// <summary>
        /// 计算字体大小
        /// </summary>
        /// <param name="candidate"></param>
        /// <param name="fontName"></param>
        /// <param name="fontSize"></param>
        /// <returns></returns>
        private Size MeasureString(string candidate, string fontName, double fontSize)
        {
            FormattedText formattedText = new FormattedText(
                candidate, 
                CultureInfo.CurrentUICulture,
                FlowDirection.LeftToRight,
                new Typeface(fontName),
                fontSize,
                Brushes.Black,
                _dpiInfo.PixelsPerDip);
            return new Size(formattedText.Width, formattedText.Height);
        }

        /// <summary>
        /// 计算值区间段
        /// </summary>
        /// <param name="max"></param>
        /// <param name="splictNumber"></param>
        /// <returns></returns>
        private double GetSegmentedValue(double max, int splictNumber)
        {
            double val = max / splictNumber;
            var exponent = Math.Floor(Math.Log(val) / Math.Log(10));
            var exp10 = Math.Pow(10, exponent);

            double f = val / exp10;
            double nf;

            if (f < 1) { nf = 1; }
            else if (f < 2) { nf = 2; }
            else if (f < 3) { nf = 3; }
            else if (f < 5) { nf = 5; }
            else { nf = 10; }

            var value = nf * exp10;

            return exponent >= -20 ? +Math.Round(value, (int)(exponent < 0 ? -exponent : 0)) : value;
        }

        /// <summary>
        /// 颜色系列
        /// </summary>
        private readonly List<Color> _colorList = new List<Color>()
        {
            Color.FromRgb(84, 112, 198),  Color.FromRgb(145, 204, 117),
            Color.FromRgb(250, 200, 88),  Color.FromRgb(238, 102, 102),
            Color.FromRgb(115, 192, 222),  Color.FromRgb(59, 162, 114),
            Color.FromRgb(252, 132, 82),  Color.FromRgb(154, 96, 180),
            Color.FromRgb(234, 124, 204)
        };

        /// <summary>
        /// Option 依赖属性
        /// </summary>
        public static readonly DependencyProperty OptionProperty = DependencyProperty.Register("Option", typeof(object), typeof(Echart), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnOptionPropertyChanged)));

        private static void OnOptionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as Echart).OnOptionPropertyChanged(e.NewValue, e.OldValue);
        }

        private void OnOptionPropertyChanged(object newValue, object oldValue)
        {
            if (newValue == oldValue) { return; }
            if (newValue is EchartOption<BarSerie> barOption)
            {
                _legendItems = new List<LegendItem>();
                if (barOption.Legend != null)
                {
                    for (int i = 0; i < barOption.Series.Count; i++)
                    {
                        _legendItems.Add(new LegendItem() { Name = barOption.Series[i].Name });
                    }
                }
                DrawBarOrLineEchart(barOption);
            }
            else if (newValue is EchartOption<PieSerie> pieOption)
            {
                _legendItems = new List<LegendItem>();
                if (pieOption.Series != null && pieOption.Series.Count == 1)
                {
                    if (pieOption.Legend != null)
                    {
                        for (int i = 0; i < pieOption.Series[0].Data.Count; i++)
                        {
                            _legendItems.Add(new LegendItem() { Name = pieOption.Series[0].Data[i].Name });
                        }
                    }
                }
                DrawPieEchart(pieOption);
            }
        }

        public object Option
        {
            get { return GetValue(OptionProperty); }
            set { SetValue(OptionProperty, value); }
        }

        public Echart()
        {
            InitializeComponent();
            lock (_sync)
                _dpiInfo = VisualTreeHelper.GetDpi(this);
            SizeChanged += Echart_SizeChanged;
        }

        /// <summary>
        /// 大小发生改变时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Echart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (Option is EchartOption<BarSerie> barOption)
            {
                DrawBarOrLineEchart(barOption);
            }
            else if (Option is EchartOption<PieSerie> pieOption)
            {
                DrawPieEchart(pieOption);
            }
        }

        /// <summary>
        /// LegendItem 鼠标左键事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LegendItemView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            StackPanel item = sender as StackPanel;
            if (item.Tag != null)
            {
                if (int.TryParse(item.Tag.ToString(), out int itenIndex))
                {
                    for (int i = 0; i < _legendItems.Count; i++)
                    {
                        if (itenIndex == i)
                        {
                            _legendItems[i].Show = !_legendItems[i].Show;
                        }
                    }

                    if (Option is EchartOption<BarSerie> barOption)
                    {
                        DrawBarOrLineEchart(barOption);
                    }
                    else if (Option is EchartOption<PieSerie> pieOption)
                    {
                        DrawPieEchart(pieOption);
                    }
                }
            }
        }

        /**
         * 柱状图或者饼状图
         */

        /// <summary>
        /// 绘制柱状或者折线图
        /// </summary>
        /// <param name="option"></param>
        private void DrawBarOrLineEchart(EchartOption<BarSerie> option)
        {
            MainCanvas.Children.Clear();

            if (option.Title == null) return;
            if (option.Series == null) return;

            Title.Text = option.Title.Text;
            Subtext.Text = option.Title.Subtext;

            double canvasWidth = MainCanvas.ActualWidth; // Canvas 的宽
            double canvasHeight = MainCanvas.ActualHeight; // Canvas 的高

            FontFamily fontFamily = new FontFamily(_defaultFontName);

            Size size = MeasureString("田", _defaultFontName, _defaultFontSize);
            double fontHeight = Math.Ceiling(size.Height); // 字体的高度

            double xAixsHeight = fontHeight + 1; // xAxis坐标内容占用的高度

            List<string> xAixs = new List<string>(); // 横坐标内容列表
            if (option.XAxis != null)
            {
                xAixs = option.XAxis.Data;
            }
            else
            {
                foreach (BarSerie serie in option.Series)
                {
                    foreach (SerieData data in serie.Data)
                    {
                        int index = xAixs.FindIndex(o => o.Equals(data.Name));
                        if (index < 0)
                        {
                            xAixs.Add(data.Name);
                        }
                    }
                }
            }

            double serieDataMaxValue = 0; // 最大值
            double serieNameMaxWidth = Math.Ceiling(MeasureString(option.Series[0].Name, _defaultFontName, _defaultFontSize).Width);
            foreach (BarSerie serie in option.Series)
            {
                double temp = Math.Ceiling(MeasureString(serie.Name, _defaultFontName, _defaultFontSize).Width);
                if (temp > serieNameMaxWidth)
                {
                    serieNameMaxWidth = temp;
                }
                foreach (SerieData data in serie.Data)
                {
                    if (data.Value > serieDataMaxValue)
                    {
                        serieDataMaxValue = data.Value;
                    }
                }
            }

            double legendTopHeight = 0;
            double legendBottomHeight = 0;
            double legendLeftWidth = 0;
            double legendRightWidth = 0;

            LegendPosition legendPosition = LegendPosition.BottomCenter;
            if (option.Legend != null)
            {
                ScrollViewer scrollViewer = new ScrollViewer() { };
                legendPosition = option.Legend.Position;
                switch (legendPosition)
                {
                    case LegendPosition.TopLeft:
                    case LegendPosition.TopCenter:
                    case LegendPosition.TopRight:
                    case LegendPosition.BottomLeft:
                    case LegendPosition.BottomCenter:
                    case LegendPosition.BottomRight:
                        scrollViewer.Width = canvasWidth;
                        scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                        scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
                        scrollViewer.PreviewMouseWheel += ScrollViewer_PreviewMouseWheel;
                        HorizontalAlignment horizontalAlignment = HorizontalAlignment.Center;
                        switch (legendPosition)
                        {
                            case LegendPosition.TopLeft:
                            case LegendPosition.BottomLeft:
                                horizontalAlignment = HorizontalAlignment.Left;
                                break;
                            case LegendPosition.TopCenter:
                            case LegendPosition.BottomCenter:
                                horizontalAlignment = HorizontalAlignment.Center;
                                break;
                            case LegendPosition.TopRight:
                            case LegendPosition.BottomRight:
                                horizontalAlignment = HorizontalAlignment.Right;
                                break;
                        }
                        StackPanel horizontalLegendPanel = new StackPanel()
                        {
                            Orientation = Orientation.Horizontal,
                            HorizontalAlignment = horizontalAlignment,
                        };

                        for (int i = 0; i < _legendItems.Count; i++)
                        {
                            LegendItem legendItem = _legendItems[i];
                            StackPanel legendItemView = new StackPanel()
                            {
                                Orientation = Orientation.Horizontal,
                                Cursor = Cursors.Hand,
                                Tag = i.ToString(),
                                Background = Brushes.Transparent,
                            };
                            legendItemView.MouseLeftButtonDown += LegendItemView_MouseLeftButtonDown;
                            if (i != 0) { legendItemView.Margin = new Thickness(5, 0, 0, 0); }
                            Border colorItem = new Border()
                            {
                                Width = 25,
                                Height = 15,
                                CornerRadius = new CornerRadius(3),
                                VerticalAlignment = VerticalAlignment.Center,
                                Background = legendItem.Show ? new SolidColorBrush(_colorList[i % _colorList.Count]) : new SolidColorBrush(Color.FromRgb(204, 204, 204))
                            };
                            TextBlock textItem = new TextBlock()
                            {
                                Text = legendItem.Name,
                                VerticalAlignment = VerticalAlignment.Center,
                                Margin = new Thickness(3, 0, 0, 0),
                            };
                            legendItemView.Children.Add(colorItem);
                            legendItemView.Children.Add(textItem);
                            horizontalLegendPanel.Children.Add(legendItemView);
                        }
                        scrollViewer.Content = horizontalLegendPanel;

                        Canvas.SetLeft(scrollViewer, 0);
                        switch (legendPosition)
                        {
                            case LegendPosition.TopLeft:
                            case LegendPosition.TopCenter:
                            case LegendPosition.TopRight:
                                legendTopHeight = _legendHeight;
                                Canvas.SetTop(scrollViewer, 0);
                                break;
                            case LegendPosition.BottomLeft:
                            case LegendPosition.BottomCenter:
                            case LegendPosition.BottomRight:
                                legendBottomHeight = _legendHeight;
                                Canvas.SetBottom(scrollViewer, 0);
                                break;
                        }
                        break;
                    case LegendPosition.LeftTop:
                    case LegendPosition.LeftCenter:
                    case LegendPosition.LeftBottom:
                    case LegendPosition.RightTop:
                    case LegendPosition.RightCenter:
                    case LegendPosition.RightBottom:
                        scrollViewer.Height = canvasHeight;
                        scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
                        scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
                        VerticalAlignment verticalAlignment = VerticalAlignment.Center;
                        switch (legendPosition)
                        {
                            case LegendPosition.TopLeft:
                            case LegendPosition.BottomLeft:
                                verticalAlignment = VerticalAlignment.Top;
                                break;
                            case LegendPosition.TopCenter:
                            case LegendPosition.BottomCenter:
                                verticalAlignment = VerticalAlignment.Center;
                                break;
                            case LegendPosition.TopRight:
                            case LegendPosition.BottomRight:
                                verticalAlignment = VerticalAlignment.Bottom;
                                break;
                        }
                        StackPanel verticalLegendPanel = new StackPanel()
                        {
                            Orientation = Orientation.Vertical,
                            VerticalAlignment = verticalAlignment,
                        };
                        for (int i = 0; i < _legendItems.Count; i++)
                        {
                            LegendItem legendItem = _legendItems[i];
                            StackPanel legendItemView = new StackPanel()
                            {
                                Orientation = Orientation.Horizontal,
                                Cursor = Cursors.Hand,
                                Tag = i.ToString(),
                                Background = Brushes.Transparent,
                            };
                            legendItemView.MouseLeftButtonDown += LegendItemView_MouseLeftButtonDown;
                            if (i != 0) { legendItemView.Margin = new Thickness(0, 5, 0, 0); }
                            Border colorItem = new Border()
                            {
                                Width = 25,
                                Height = 15,
                                CornerRadius = new CornerRadius(3),
                                VerticalAlignment = VerticalAlignment.Center,
                                Background = legendItem.Show ? new SolidColorBrush(_colorList[i % _colorList.Count]) : new SolidColorBrush(Color.FromRgb(204, 204, 204))
                            };
                            TextBlock textItem = new TextBlock()
                            {
                                Text = legendItem.Name,
                                VerticalAlignment = VerticalAlignment.Center,
                                Margin = new Thickness(3, 0, 0, 0),
                            };
                            legendItemView.Children.Add(colorItem);
                            legendItemView.Children.Add(textItem);
                            verticalLegendPanel.Children.Add(legendItemView);
                        }
                        scrollViewer.Content = verticalLegendPanel;
                        Canvas.SetTop(scrollViewer, 0);
                        switch (legendPosition)
                        {
                            case LegendPosition.LeftTop:
                            case LegendPosition.LeftCenter:
                            case LegendPosition.LeftBottom:
                                legendLeftWidth = 25 + 3 + serieNameMaxWidth + 10;
                                Canvas.SetLeft(scrollViewer, 0);
                                break;
                            case LegendPosition.RightTop:
                            case LegendPosition.RightCenter:
                            case LegendPosition.RightBottom:
                                legendRightWidth = 25 + 3 + serieNameMaxWidth + 10;
                                Canvas.SetRight(scrollViewer, 0);
                                break;
                        }
                        break;
                }
                MainCanvas.Children.Add(scrollViewer);
            }

            int yAxisSplictRangeNum = serieDataMaxValue <= 30 ? 3 : serieDataMaxValue >= 2000 ? 5 : 4; // 值划分区间段数
            double seg = GetSegmentedValue(serieDataMaxValue, yAxisSplictRangeNum);
            int yAxisSplictRangeLineNum = yAxisSplictRangeNum + 1; // 值划分区间段划线数

            double yAxisMaxRange = yAxisSplictRangeNum * seg; // 值坐标的区间段最大值

            double yAxisMaxRangeTextWidth = 0; // 值坐标的区间段最大值的宽度
            for (int i = 0; i < yAxisSplictRangeLineNum; i++)
            {
                double width = MeasureString((seg * i).ToString(), _defaultFontName, _defaultFontSize).Width;
                if(width > yAxisMaxRangeTextWidth) { yAxisMaxRangeTextWidth = width; }
            }
            yAxisMaxRangeTextWidth = Math.Ceiling(yAxisMaxRangeTextWidth);

            Dictionary<string, List<BarLineContext>> linePoints = new Dictionary<string, List<BarLineContext>>();
            int eachXAisBarNum = 0; // 每个横坐标的柱子数
            for(int i = 0; i < option.Series.Count; i++)
            {
                BarSerie serie = option.Series[i];
                if (serie.Type == SerieType.Bar)
                {
                    eachXAisBarNum++;
                }
                else if (serie.Type == SerieType.Line)
                {
                    linePoints.Add(i.ToString(), new List<BarLineContext>());
                }
            }
            
            double xAxisWidth = Math.Floor((canvasWidth - yAxisMaxRangeTextWidth - legendLeftWidth - legendRightWidth) / xAixs.Count); // xAxis每个坐标系的宽度

            double barWidth = _defaultBarWidth; // 柱子宽度

            if (xAxisWidth < (barWidth * eachXAisBarNum + _minSpaceWidth))
            {
                barWidth = (xAxisWidth - _minSpaceWidth) / eachXAisBarNum;
                if (barWidth <= _minBarWidth)
                {
                    barWidth = _minBarWidth;
                }
            }

            if (xAixs.Count > 0)
            {
                double rectWidth = Math.Ceiling(MeasureString(xAixs[0], _defaultFontName, _defaultFontSize).Width);
                for (int i = 0; i < xAixs.Count; i++)
                {
                    double temp = Math.Ceiling(MeasureString(xAixs[i], _defaultFontName, _defaultFontSize).Width);
                    if (temp > rectWidth)
                    {
                        rectWidth = temp;
                    }
                }
                for (int i = 0; i < xAixs.Count; i++)
                {
                    double textWidth = MeasureString(xAixs[i], _defaultFontName, _defaultFontSize).Width;
                    double startPointX = xAxisWidth * i + yAxisMaxRangeTextWidth + 5 + legendLeftWidth;
                    if (textWidth < xAxisWidth)
                    {
                        startPointX += (xAxisWidth - textWidth) / 2;
                    }

                    double hl = 0;
                    if (option.XAxis != null && option.XAxis.AxisLabel != null && option.XAxis.AxisLabel.Rotate != 0)
                    {
                        hl = Math.Sin(2 * Math.PI * option.XAxis.AxisLabel.Rotate / 360) * (fontHeight + 1);

                        double rectHeight = fontHeight + 1;

                        xAixsHeight = Math.Sin(2 * Math.PI * option.XAxis.AxisLabel.Rotate / 360) * rectWidth + Math.Cos(2 * Math.PI * option.XAxis.AxisLabel.Rotate / 360) * rectHeight;

                        startPointX = xAxisWidth * i + yAxisMaxRangeTextWidth + 5 + legendLeftWidth + xAxisWidth / 2;
                    }

                    double startPointY = canvasHeight - xAixsHeight - legendBottomHeight;

                    Point startPoint = new Point(startPointX, startPointY);

                    PathGeometry xAixsLabelTextGeo = DrawBarLineText(xAixs[i], startPoint);

                    Path xAixsLabelTextPath = new Path()
                    {
                        Fill = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)),
                        Data = xAixsLabelTextGeo,
                        Cursor = Cursors.Hand,
                    };
                    if (option.XAxis != null && option.XAxis.AxisLabel != null && option.XAxis.AxisLabel.Rotate != 0)
                    {
                        xAixsLabelTextPath.RenderTransform = new RotateTransform(option.XAxis.AxisLabel.Rotate, startPointX, startPointY);
                    }

                    MainCanvas.Children.Add(xAixsLabelTextPath);
                }
            }

            /**
             * 画柱子区域的高度
             * canvasHeight Canvas的高度
             * xAixsHeight xAxis坐标内容的高度
             * (fontHeight + 1) / 2 yAixs坐标的最大值的高度的一半
             */
            double barSpaceHeight = canvasHeight - xAixsHeight - (fontHeight + 1) / 2 - legendBottomHeight - legendTopHeight;

            // 画YAxis的区间段线
            for (int i = 0; i < yAxisSplictRangeLineNum; i++)
            {
                double textWidth = MeasureString((seg * i).ToString(), _defaultFontName, _defaultFontSize).Width;

                double labelX = legendLeftWidth;
                double labelY = barSpaceHeight - barSpaceHeight * Convert.ToDouble(i) / (yAxisSplictRangeLineNum - 1) + legendTopHeight;
                if (textWidth < yAxisMaxRangeTextWidth)
                {
                    labelX += yAxisMaxRangeTextWidth - textWidth;
                }
                PathGeometry yAixsLabelTextGeo = DrawBarLineText((seg * i).ToString(), new Point(labelX, labelY));

                Path yAixsLabelTextPath = new Path()
                {
                    Fill = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)),
                    Data = yAixsLabelTextGeo,
                    Cursor = Cursors.Hand,
                };

                MainCanvas.Children.Add(yAixsLabelTextPath);

                Path path = new Path
                {
                    Stroke = new SolidColorBrush(Color.FromRgb(217, 217, 217)),
                    StrokeThickness = 1
                };

                Point startPoint = new Point(yAxisMaxRangeTextWidth + 5 + legendLeftWidth, barSpaceHeight - barSpaceHeight * Convert.ToDouble(i) / (yAxisSplictRangeLineNum - 1) + (fontHeight + 1) / 2 + legendTopHeight);
                Point endPoint = new Point(canvasWidth - 2 - legendRightWidth, barSpaceHeight - barSpaceHeight * Convert.ToDouble(i) / (yAxisSplictRangeLineNum - 1) + (fontHeight + 1) / 2 + legendTopHeight);
                path.Data = new PathGeometry()
                {
                    Figures = new PathFigureCollection()
                    {
                        new PathFigure()
                        {
                            IsClosed = false,
                            StartPoint = startPoint,
                            Segments = new PathSegmentCollection()
                            {
                                new LineSegment() { Point = endPoint }
                            }
                        }
                    }
                };
                MainCanvas.Children.Add(path);
            }

            for (int i = 0; i < xAixs.Count; i++)
            {
                string xAixsName = xAixs[i];

                int calculateBarWidthNum = 0;
                if (option.Legend != null)
                {
                    calculateBarWidthNum = 0;
                    for (int k = 0; k < _legendItems.Count; k++)
                    {
                        LegendItem item = _legendItems[k];
                        BarSerie serie = option.Series[k];
                        if (serie.Type == SerieType.Bar && item.Show)
                        {
                            calculateBarWidthNum++;
                        }
                    }
                }
                else
                {
                    foreach(BarSerie serie in option.Series)
                    {
                        if (serie.Type == SerieType.Bar )
                        {
                            calculateBarWidthNum++;
                        }
                    }
                }

                double barBoxWidth = barWidth * calculateBarWidthNum;

                List<BarLineContext> barLineContexts = new List<BarLineContext>();

                int index = 0;
                for (int j = 0; j < option.Series.Count; j++)
                {
                    if (_legendItems.Count == option.Series.Count && !_legendItems[j].Show)
                    {
                        continue;
                    }

                    BarSerie serie = option.Series[j];
                    double? value = null;
                    foreach (SerieData data in serie.Data)
                    {
                        value = null;
                        if (!string.IsNullOrEmpty(data.Name) && data.Name.Equals(xAixsName))
                        {
                            value = data.Value;
                            break;
                        }
                    }
                    if (value.HasValue)
                    {
                        switch (serie.Type)
                        {
                            case SerieType.Bar:
                                double barStartPointX = ((xAxisWidth - barBoxWidth) / 2) + (xAxisWidth * i) + yAxisMaxRangeTextWidth + 5 + legendLeftWidth + (barWidth * index);
                                double barStartPointY = (fontHeight + 1) / 2 + legendTopHeight + (barSpaceHeight - (value.Value / yAxisMaxRange) * barSpaceHeight);

                                double barEndPointX = barStartPointX + barWidth;
                                double barEndPointY = (fontHeight + 1) / 2 + legendTopHeight + barSpaceHeight;
                                Rect rect = new Rect(new Point(barStartPointX, barStartPointY), new Point(barEndPointX, barEndPointY));
                                RectangleGeometry barGeo = new RectangleGeometry()
                                {
                                    Rect = rect
                                };
                                BarLineContext barContext = new BarLineContext()
                                {
                                    SerieName = xAixsName,
                                    Name = serie.Name,
                                    Value = value.Value,
                                    HasValue = true,
                                    Color = new SolidColorBrush(_colorList[j % _colorList.Count]),
                                    Tooltip = option.Tooltip,
                                    Type = SerieType.Bar,
                                    Rect = rect,
                                    Label = serie.Label
                                };
                                Path barPath = new Path()
                                {
                                    Fill = new SolidColorBrush(_colorList[j % _colorList.Count]),
                                    Data = barGeo,
                                    DataContext = barContext,
                                    Cursor = Cursors.Hand,
                                };
                                MainCanvas.Children.Add(barPath);

                                if (barContext.Label != null && barContext.Label.Show)
                                {
                                    PathGeometry textGeo = DrawBarLineSerieLabelText(barPath);

                                    Path textPath = new Path()
                                    {
                                        Fill = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)),
                                        Data = textGeo,
                                        Cursor = Cursors.Hand,
                                    };

                                    MainCanvas.Children.Add(textPath);
                                }

                                barLineContexts.Add(barContext);
                                break;
                            case SerieType.Line:

                                double linePointX = (xAxisWidth - barBoxWidth) / 2 + xAxisWidth * i + yAxisMaxRangeTextWidth + 5 + legendLeftWidth + (barBoxWidth / 2);
                                double linePointY = (fontHeight + 1) / 2 + legendTopHeight + (barSpaceHeight - (value.Value / yAxisMaxRange) * barSpaceHeight);

                                Point linePoint = new Point(linePointX, linePointY);

                                BarLineContext lineContext = new BarLineContext()
                                {
                                    SerieName = xAixsName,
                                    Name = serie.Name,
                                    Value = value.Value,
                                    HasValue = true,
                                    Color = new SolidColorBrush(_colorList[j % _colorList.Count]),
                                    Tooltip = option.Tooltip,
                                    Type = SerieType.Line,
                                    LinePoint = linePoint,
                                    Top = (fontHeight + 1) / 2 + legendTopHeight,
                                    Bottom = (fontHeight + 1) / 2 + legendTopHeight + barSpaceHeight,
                                    Label = serie.Label
                                };

                                linePoints[j.ToString()].Add(lineContext);

                                barLineContexts.Add(lineContext);

                                if (i == xAixs.Count - 1)
                                {
                                    PointCollection polygonPoints = new PointCollection();
                                    foreach (BarLineContext context in linePoints[j.ToString()])
                                    {
                                        polygonPoints.Add(context.LinePoint);
                                    }

                                    Polyline polyline = new Polyline()
                                    {
                                        Stroke = new SolidColorBrush(_colorList[j % _colorList.Count]),
                                        Points = polygonPoints
                                    };

                                    MainCanvas.Children.Add(polyline);

                                    foreach (BarLineContext context in linePoints[j.ToString()])
                                    {
                                        EllipseGeometry linePointGeo = new EllipseGeometry()
                                        {
                                            RadiusX = 2,
                                            RadiusY = 2,
                                            Center = context.LinePoint,

                                        };

                                        Path linePointPath = new Path()
                                        {
                                            Fill = Brushes.White,
                                            Data = linePointGeo,
                                            Stroke = new SolidColorBrush(_colorList[j % _colorList.Count]),
                                            StrokeThickness = 1,
                                            Cursor = Cursors.Hand,
                                            DataContext = context
                                        };

                                        MainCanvas.Children.Add(linePointPath);


                                        if(context.Label != null && context.Label.Show)
                                        {
                                            PathGeometry lineTextGeo = DrawBarLineSerieLabelText(linePointPath);

                                            Path lineTextPath = new Path()
                                            {
                                                Fill = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)),
                                                Data = lineTextGeo,
                                                Cursor = Cursors.Hand,
                                            };

                                            MainCanvas.Children.Add(lineTextPath);
                                        }
                                    }
                                }
                                break;
                        }
                    }
                    else
                    {
                        BarLineContext barContext = new BarLineContext() { 
                            SerieName = xAixsName, 
                            Name = serie.Name, 
                            HasValue = false, 
                            Color = new SolidColorBrush(_colorList[j % _colorList.Count]), 
                            Tooltip = option.Tooltip,
                            Type = serie.Type,
                        };
                        barLineContexts.Add(barContext);
                    }
                    index++;
                }

                double barRectStartPointX = legendLeftWidth + yAxisMaxRangeTextWidth + 5 + xAxisWidth * i;
                double barRectStartPointY = (fontHeight + 1) / 2 + legendTopHeight;

                double barRectEndPointX = barRectStartPointX + xAxisWidth;
                double barRectEndPointY = (fontHeight + 1) / 2 + legendTopHeight + barSpaceHeight;

                RectangleGeometry boxRectGeo = new RectangleGeometry()
                {
                    Rect = new Rect(new Point(barRectStartPointX, barRectStartPointY), new Point(barRectEndPointX, barRectEndPointY))
                };

                Path boxRectPath = new Path()
                {
                    Fill = Brushes.Transparent,
                    Data = boxRectGeo,
                    DataContext = barLineContexts,
                    Cursor = Cursors.Hand,
                };

                boxRectPath.MouseMove += BoxRectPath_MouseMove;
                boxRectPath.MouseLeave += BoxRectPath_MouseLeave;

                MainCanvas.Children.Add(boxRectPath);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="text"></param>
        /// <param name="startPoint"></param>
        /// <returns></returns>
        PathGeometry DrawBarLineText(string text, Point startPoint)
        {
            NameScope.SetNameScope(this, new NameScope());

            Typeface typeface = new Typeface
                (new FontFamily(_defaultFontName),
                FontStyles.Normal,
                FontWeights.Normal, 
                FontStretches.Normal);

            FormattedText formattedText = new FormattedText(
              text,
              CultureInfo.CurrentUICulture,
              FlowDirection.LeftToRight,
              typeface,
              _defaultFontSize,
              Brushes.Black,
              _dpiInfo.PixelsPerDip);

            Geometry geo = formattedText.BuildGeometry(startPoint);

            PathGeometry pathGeometry = geo.GetFlattenedPathGeometry();

            return pathGeometry;
        }

        /// <summary>
        /// 画饼状图文本
        /// </summary>
        /// <param name="path"></param>
        /// <param name="fontsize"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        PathGeometry DrawBarLineSerieLabelText(Path path)
        {
            NameScope.SetNameScope(this, new NameScope());
            BarLineContext context = path.DataContext as BarLineContext;

            Typeface typeface = new Typeface
                (new FontFamily(_defaultFontName),
                FontStyles.Normal,
                FontWeights.Normal, FontStretches.Normal);

            FormattedText formattedText = new FormattedText(
               context.Value.ToString(),
               CultureInfo.CurrentUICulture,
               FlowDirection.LeftToRight,
               typeface,
               _defaultFontSize,
               Brushes.Black,
               _dpiInfo.PixelsPerDip);

            double textWidth = formattedText.Width;
            double textHeight = formattedText.Height;

            Geometry geo;
            switch (context.Type)
            {
                case SerieType.Line:
                    geo = formattedText.BuildGeometry(new Point(context.LinePoint.X - (textWidth / 2), context.LinePoint.Y - _defaultFontSize / 1.8 - textHeight));
                    break;
                case SerieType.Bar:
                default:
                    double rectWidth = context.Rect.Width;
                    double offsetX = (rectWidth - textWidth) / 2;

                    geo = formattedText.BuildGeometry(new Point(context.Rect.X + offsetX, context.Rect.Y - _defaultFontSize / 1.8 - textHeight));
                    break;
            }
            
            PathGeometry pathGeometry = geo.GetFlattenedPathGeometry();

            return pathGeometry;
        }

        private Path _line;

        /// <summary>
        /// 当鼠标离开boxRectPath时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BoxRectPath_MouseLeave(object sender, MouseEventArgs e)
        {
            Path p = sender as Path;
            p.Fill = Brushes.Transparent;

            PART_Popup.IsOpen = false;

            if(_line != null)
            {
                MainCanvas.Children.Remove(_line);
            }
        }

        /// <summary>
        /// 当鼠标在BoxRectPath上移动时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BoxRectPath_MouseMove(object sender, MouseEventArgs e)
        {
            Path p = sender as Path;

            Point mousePosition = e.GetPosition((UIElement)MainCanvas.Parent);
            if (!PART_Popup.IsOpen)
            {
                PART_Popup.IsOpen = true;
            }

            p.Fill = new SolidColorBrush(Color.FromArgb(125, 217, 217, 217));

            Point lineTopPoint;
            Point lineBottomPoint;

            if (p.DataContext is List<BarLineContext> barLineContexts && barLineContexts.Count > 0 && barLineContexts[0].Tooltip != null && barLineContexts[0].Tooltip.Show)
            {
                StackPanel pc = new StackPanel() { Orientation = Orientation.Vertical };

                TextBlock st = new TextBlock() { Text = barLineContexts[0].SerieName, Margin = new Thickness(0, 0, 0, 3), FontWeight = FontWeights.Black };
                pc.Children.Add(st);

                foreach (var context in barLineContexts)
                {
                    StackPanel ch = new StackPanel() { Orientation = Orientation.Horizontal };
                    Border r = new Border() { Width = 10, Height = 10, CornerRadius = new CornerRadius(5), VerticalAlignment = VerticalAlignment.Center, Background = context.Color, Margin = new Thickness(0, 0, 5, 0) };

                    string text;
                    if (context.HasValue)
                    {
                        if (!string.IsNullOrEmpty(context.Tooltip.Formatter))
                        {
                            text = string.IsNullOrEmpty(context.Name) ? $"{context.Value.ToString(context.Tooltip.Formatter)}" : $"{context.Name} : {context.Value.ToString(context.Tooltip.Formatter)}";
                        }
                        else
                        {
                            text = string.IsNullOrEmpty(context.Name) ? $"{context.Value}" : $"{context.Name} : {context.Value}";
                        }
                    }
                    else
                    {
                        text = string.IsNullOrEmpty(context.Name) ? $"_" : $"{context.Name} : _";
                    }

                    TextBlock t = new TextBlock() { Text = text, VerticalAlignment = VerticalAlignment.Center };
                    ch.Children.Add(r);
                    ch.Children.Add(t);
                    pc.Children.Add(ch);

                    if (context.Type == SerieType.Line)
                    {
                        lineTopPoint = new Point(context.LinePoint.X, context.Top);
                        lineBottomPoint = new Point(context.LinePoint.X, context.Bottom);
                    }
                }

                Border border = new Border
                {
                    Effect = new DropShadowEffect() { BlurRadius = 15, Opacity = 0.3, ShadowDepth = 0 },
                    Margin = new Thickness(15),
                    Child = pc,
                    Background = Brushes.White,
                    Padding = new Thickness(5),
                    CornerRadius = new CornerRadius(3)
                };
                System.Windows.Controls.Grid grid = new System.Windows.Controls.Grid();
                grid.Children.Add(border);

                PART_Popup.Child = grid;
                PART_Popup.HorizontalOffset = mousePosition.X + 5;
                PART_Popup.VerticalOffset = mousePosition.Y + 5;
                PART_Popup.IsOpen = true;

                if (lineTopPoint != null && lineBottomPoint != null)
                {
                    if(_line != null)
                    {
                        MainCanvas.Children.Remove(_line);
                    }
                    List<double> dash = new List<double> { 8,8 };
                    _line = new Path()
                    {
                        Data = new PathGeometry()
                        {
                            Figures = new PathFigureCollection()
                            {
                                new PathFigure()
                                {
                                    IsClosed = false,
                                    StartPoint = lineTopPoint,
                                    IsFilled = false,
                                    Segments = new PathSegmentCollection()
                                    {
                                        new LineSegment() { Point = lineBottomPoint }
                                    }
                                }
                            }
                        },
                        Stroke = Brushes.Gray,
                        StrokeThickness = 0.5,
                        StrokeDashArray = new DoubleCollection(dash),
                        StrokeDashCap = PenLineCap.Square
                    };
                    MainCanvas.Children.Add(_line);
                }
            }
        }

        /// <summary>
        /// 柱状图折线图的legend的滚动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (sender is ScrollViewer scroll)
            {
                int d = e.Delta;
                if (d > 0)
                {
                    scroll.LineLeft();
                }
                if (d < 0)
                {

                    scroll.LineRight();
                }
                scroll.ScrollToTop();
            }
        }

        /**
         * 饼状图
         */
        private bool _flg = false;

        /// <summary>
        /// 绘制饼状图
        /// </summary>
        /// <param name="option"></param>
        private void DrawPieEchart(EchartOption<PieSerie> option)
        {
            MainCanvas.Children.Clear();

            if (option.Title == null) return;
            if (option.Series == null) return;

            Title.Text = option.Title.Text;
            Subtext.Text = option.Title.Subtext;

            double canvasWidth = MainCanvas.ActualWidth; // Canvas 的宽
            double canvasHeight = MainCanvas.ActualHeight; // Canvas 的高

            LegendPosition legendPosition = LegendPosition.BottomCenter;
            if (option.Legend != null && option.Series.Count == 1)
            {
                ScrollViewer scrollViewer = new ScrollViewer() { };
                legendPosition = option.Legend.Position;
                switch (legendPosition)
                {
                    case LegendPosition.TopLeft:
                    case LegendPosition.TopCenter:
                    case LegendPosition.TopRight:
                    case LegendPosition.BottomLeft:
                    case LegendPosition.BottomCenter:
                    case LegendPosition.BottomRight:
                        scrollViewer.Width = canvasWidth;
                        scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                        scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
                        scrollViewer.PreviewMouseWheel += ScrollViewer_PreviewMouseWheel;
                        HorizontalAlignment horizontalAlignment = HorizontalAlignment.Center;
                        switch (legendPosition)
                        {
                            case LegendPosition.TopLeft:
                            case LegendPosition.BottomLeft:
                                horizontalAlignment = HorizontalAlignment.Left;
                                break;
                            case LegendPosition.TopCenter:
                            case LegendPosition.BottomCenter:
                                horizontalAlignment = HorizontalAlignment.Center;
                                break;
                            case LegendPosition.TopRight:
                            case LegendPosition.BottomRight:
                                horizontalAlignment = HorizontalAlignment.Right;
                                break;
                        }
                        StackPanel horizontalLegendPanel = new StackPanel()
                        {
                            Orientation = Orientation.Horizontal,
                            HorizontalAlignment = horizontalAlignment,
                        };

                        for (int i = 0; i < _legendItems.Count; i++)
                        {
                            LegendItem legendItem = _legendItems[i];
                            StackPanel legendItemView = new StackPanel()
                            {
                                Orientation = Orientation.Horizontal,
                                Cursor = Cursors.Hand,
                                Tag = i.ToString(),
                                Background = Brushes.Transparent,
                            };
                            legendItemView.MouseLeftButtonDown += LegendItemView_MouseLeftButtonDown;
                            if (i != 0) { legendItemView.Margin = new Thickness(5, 0, 0, 0); }
                            Border colorItem = new Border()
                            {
                                Width = 25,
                                Height = 15,
                                CornerRadius = new CornerRadius(3),
                                VerticalAlignment = VerticalAlignment.Center,
                                Background = legendItem.Show ? new SolidColorBrush(_colorList[i % _colorList.Count]) : new SolidColorBrush(Color.FromRgb(204, 204, 204))
                            };
                            TextBlock textItem = new TextBlock()
                            {
                                Text = legendItem.Name,
                                VerticalAlignment = VerticalAlignment.Center,
                                Margin = new Thickness(3, 0, 0, 0),
                            };
                            legendItemView.Children.Add(colorItem);
                            legendItemView.Children.Add(textItem);
                            horizontalLegendPanel.Children.Add(legendItemView);
                        }
                        scrollViewer.Content = horizontalLegendPanel;

                        Canvas.SetLeft(scrollViewer, 0);
                        switch (legendPosition)
                        {
                            case LegendPosition.TopLeft:
                            case LegendPosition.TopCenter:
                            case LegendPosition.TopRight:
                                Canvas.SetTop(scrollViewer, 0);
                                break;
                            case LegendPosition.BottomLeft:
                            case LegendPosition.BottomCenter:
                            case LegendPosition.BottomRight:
                                Canvas.SetBottom(scrollViewer, 0);
                                break;
                        }
                        break;
                    case LegendPosition.LeftTop:
                    case LegendPosition.LeftCenter:
                    case LegendPosition.LeftBottom:
                    case LegendPosition.RightTop:
                    case LegendPosition.RightCenter:
                    case LegendPosition.RightBottom:
                        scrollViewer.Height = canvasHeight;
                        scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
                        scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
                        VerticalAlignment verticalAlignment = VerticalAlignment.Center;
                        switch (legendPosition)
                        {
                            case LegendPosition.TopLeft:
                            case LegendPosition.BottomLeft:
                                verticalAlignment = VerticalAlignment.Top;
                                break;
                            case LegendPosition.TopCenter:
                            case LegendPosition.BottomCenter:
                                verticalAlignment = VerticalAlignment.Center;
                                break;
                            case LegendPosition.TopRight:
                            case LegendPosition.BottomRight:
                                verticalAlignment = VerticalAlignment.Bottom;
                                break;
                        }
                        StackPanel verticalLegendPanel = new StackPanel()
                        {
                            Orientation = Orientation.Vertical,
                            VerticalAlignment = verticalAlignment,
                        };
                        for (int i = 0; i < _legendItems.Count; i++)
                        {
                            LegendItem legendItem = _legendItems[i];
                            StackPanel legendItemView = new StackPanel()
                            {
                                Orientation = Orientation.Horizontal,
                                Cursor = Cursors.Hand,
                                Tag = i.ToString(),
                                Background = Brushes.Transparent,
                            };
                            legendItemView.MouseLeftButtonDown += LegendItemView_MouseLeftButtonDown;
                            if (i != 0) { legendItemView.Margin = new Thickness(0, 5, 0, 0); }
                            Border colorItem = new Border()
                            {
                                Width = 25,
                                Height = 15,
                                CornerRadius = new CornerRadius(3),
                                VerticalAlignment = VerticalAlignment.Center,
                                Background = legendItem.Show ? new SolidColorBrush(_colorList[i % _colorList.Count]) : new SolidColorBrush(Color.FromRgb(204, 204, 204))
                            };
                            TextBlock textItem = new TextBlock()
                            {
                                Text = legendItem.Name,
                                VerticalAlignment = VerticalAlignment.Center,
                                Margin = new Thickness(3, 0, 0, 0),
                            };
                            legendItemView.Children.Add(colorItem);
                            legendItemView.Children.Add(textItem);
                            verticalLegendPanel.Children.Add(legendItemView);
                        }
                        scrollViewer.Content = verticalLegendPanel;
                        Canvas.SetTop(scrollViewer, 0);
                        switch (legendPosition)
                        {
                            case LegendPosition.LeftTop:
                            case LegendPosition.LeftCenter:
                            case LegendPosition.LeftBottom:
                                Canvas.SetLeft(scrollViewer, 0);
                                break;
                            case LegendPosition.RightTop:
                            case LegendPosition.RightCenter:
                            case LegendPosition.RightBottom:
                                Canvas.SetRight(scrollViewer, 0);
                                break;
                        }
                        break;
                }
                MainCanvas.Children.Add(scrollViewer);
            }

            for (int i = 0; i < option.Series.Count; i++)
            {
                double centerX = canvasWidth / 2; // 默认取Canvas的中点
                double centerY = canvasHeight / 2; // 默认取Canvas的中点

                PieSerie serie = option.Series[i];
                double sum = serie.Data.Select(o => o.Value).Sum();
                double seriseDataShowCount = serie.Data.Count;
                if (option.Series.Count == 1)
                {
                    sum = 0;
                    seriseDataShowCount = 0;
                    for (int k = 0; k < _legendItems.Count; k++)
                    {
                        LegendItem item = _legendItems[k];
                        SerieData data = serie.Data[k];
                        if (item.Show)
                        {
                            sum += data.Value;
                            seriseDataShowCount++;
                        }
                    }
                }

                if (sum == 0) return;

                double radiusRatio = string.IsNullOrEmpty(serie.Radius) ? 1 : double.Parse(serie.Radius.TrimEnd(new char[] { '%', ' ' })) / 100;

                double pieWidth = 0, pieHeight = 0;
                pieWidth = pieHeight = canvasWidth > canvasHeight ? canvasHeight * radiusRatio : canvasWidth * radiusRatio;

                double radius = pieWidth / 2;
                double angle = 0;
                double prevAngle = 0;

                for (int j = 0; j < serie.Data.Count; j++)
                {
                    if (_legendItems.Count == serie.Data.Count && !_legendItems[j].Show) continue;

                    SerieData data = serie.Data[j];

                    double line1X = radius * Math.Cos(angle * Math.PI / 180) + centerX;
                    double line1Y = radius * Math.Sin(angle * Math.PI / 180) + centerY;

                    angle = data.Value / sum * 360 + prevAngle;

                    double arcX = 0, arcY = 0;

                    if((seriseDataShowCount == 1 && angle == 360) || angle == 360)
                    {
                        arcX = centerX + Math.Cos(359.99999 * Math.PI / 180) * radius;
                        arcY = (radius * Math.Sin(359.99999 * Math.PI / 180)) + centerY;
                    }
                    else
                    {
                        arcX = centerX + Math.Cos(angle * Math.PI / 180) * radius;
                        arcY = (radius * Math.Sin(angle * Math.PI / 180)) + centerY;
                    }

                    LineSegment line1Segment = new LineSegment(new Point(line1X, line1Y), false);

                    bool isLargeArc = data.Value / sum > 0.5;

                    double arcWidth = radius, arcHeight = radius;

                    ArcSegment arcSegment = new ArcSegment
                    {
                        Size = new Size(arcWidth, arcHeight),
                        Point = new Point(arcX, arcY),
                        SweepDirection = SweepDirection.Clockwise,
                        IsLargeArc = isLargeArc
                    };

                    var line2Segment = new LineSegment(new Point(centerX, centerY), false);

                    PieContext piebase = new PieContext
                    {
                        Sum = sum,
                        Radius = radius,
                        Name = data.Name,
                        Value = data.Value,
                        Color = new SolidColorBrush(_colorList[j % _colorList.Count]),
                        LineSegmentStar = line1Segment,
                        ArcSegment = arcSegment,
                        LineSegmentEnd = line2Segment,
                        Angle = data.Value / sum * 360,
                        StarPoint = new Point(line1X, line1Y),
                        EndPoint = new Point(arcX, arcY),
                        CenterPoint = new Point(centerX, centerY),
                        SerieName = serie.Name,
                        Tooltip = option.Tooltip
                    };

                    PathFigure pathFigure = new PathFigure(new Point(centerX, centerY), new List<PathSegment>()
                    {
                        line1Segment,
                        arcSegment,
                        line2Segment,
                    }, true);

                    List<PathFigure> pathFigures = new List<PathFigure>()
                    {
                        pathFigure,
                    };
                    PathGeometry pathGeometry = new PathGeometry(pathFigures);
                    Path path = new Path
                    {
                        Fill = new SolidColorBrush(_colorList[j % _colorList.Count]),
                        Data = pathGeometry,
                        DataContext = piebase,
                        Cursor = Cursors.Hand
                    };
                    MainCanvas.Children.Add(path);

                    prevAngle = angle;

                    Polyline line3 = PieDrawLine(path);
                    if (line3 != null)
                        piebase.Line = line3;
                    PathGeometry textPathGeo = PieDrawText(path);
                    Path textpath = new Path() { 
                        Fill = new SolidColorBrush(_colorList[j % _colorList.Count]), 
                        Data = textPathGeo,
                    };
                    piebase.TextPath = textpath;

                    MainCanvas.Children.Add(textpath);

                    path.MouseMove += PiePath_MouseMove;
                    path.MouseLeave += PiePath_MouseLeave;

                    if ((seriseDataShowCount == 1 && angle == 360) || angle == 360)
                    {
                        MainCanvas.Children.Add(line3);
                    }
                    else
                    {
                        var outline1 = new Line()
                        {
                            X1 = centerX,
                            Y1 = centerY,
                            X2 = line1Segment.Point.X,
                            Y2 = line1Segment.Point.Y,
                            Stroke = Brushes.White,
                            StrokeThickness = 0.8,
                        };
                        var outline2 = new Line()
                        {
                            X1 = centerX,
                            Y1 = centerY,
                            X2 = arcSegment.Point.X,
                            Y2 = arcSegment.Point.Y,
                            Stroke = Brushes.White,
                            StrokeThickness = 0.8,
                        };
                        MainCanvas.Children.Add(outline1);
                        MainCanvas.Children.Add(outline2);
                        MainCanvas.Children.Add(line3);
                    }
                }
            }
        }

        /// <summary>
        /// 画饼状图指示线
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        Polyline PieDrawLine(Path path)
        {
            NameScope.SetNameScope(this, new NameScope());
            PieContext pieContext = path.DataContext as PieContext;
            double centerX = pieContext.CenterPoint.X;
            double centerY = pieContext.CenterPoint.Y;
            double angle = pieContext.Angle;
            double radius = pieContext.Radius;
            pieContext.Line = null;
            Point minPoint = new Point(Math.Round(pieContext.StarPoint.X + pieContext.EndPoint.X) / 2, Math.Round(pieContext.StarPoint.Y + pieContext.EndPoint.Y) / 2);

            Vector v1;
            if (angle > 180 && angle < 360)
            {
                v1 = new Point(centerX, centerY) - minPoint;
            }
            else if (angle == 180 || angle == 360)
            {
                if (Math.Round(pieContext.StarPoint.X) == Math.Round(pieContext.EndPoint.X))
                {
                    v1 = new Point(radius * 2, radius) - new Point(centerX, centerY);

                }
                else
                {
                    if (Math.Round(pieContext.StarPoint.X) - Math.Round(pieContext.EndPoint.X) == 2 * radius)
                    {
                        v1 = new Point(radius, 2 * radius) - new Point(centerX, centerY);
                    }
                    else
                    {
                        v1 = new Point(radius, 0) - new Point(centerX, centerY);
                    }
                }
            }
            else
            {
                v1 = minPoint - new Point(centerX, centerY);
            }
            v1.Normalize();
            Vector Vmin = v1 * radius;
            Point RadiusToNodal = Vmin + new Point(centerX, centerY);
            Vector v2 = new Point(2000, 0) - new Point(0, 0);
            double vAngle = Math.Round(Vector.AngleBetween(v2, v1));

            double offsetX = 10 * Math.Cos(vAngle * Math.PI / 180);
            double offsetY = 10 * Math.Sin(vAngle * Math.PI / 180);

            Point prolongPoint = new Point(RadiusToNodal.X + offsetX * 1, RadiusToNodal.Y + offsetY * 1);

            if (RadiusToNodal.X == double.NaN || RadiusToNodal.Y == double.NaN || prolongPoint.X == double.NaN || prolongPoint.Y == double.NaN)
                return null;


            Point point1 = RadiusToNodal;
            Point point2 = prolongPoint;
            Point point3;

            if (prolongPoint.X >= centerX)
                point3 = new Point(prolongPoint.X + 10, prolongPoint.Y);
            else
                point3 = new Point(prolongPoint.X - 10, prolongPoint.Y);

            PointCollection polygonPoints = new PointCollection
            {
                point1,
                point2,
                point3
            };

            Polyline line3 = new Polyline
            {
                Points = polygonPoints,
                Stroke = pieContext.Color
            };
            pieContext.PolylineEndPoint = point3;

            return line3;
        }

        /// <summary>
        /// 画饼状图文本
        /// </summary>
        /// <param name="path"></param>
        /// <param name="fontsize"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        PathGeometry PieDrawText(Path path)
        {
            NameScope.SetNameScope(this, new NameScope());
            PieContext pieContext = path.DataContext as PieContext;
            double centerX = pieContext.CenterPoint.X;

            Typeface typeface = new Typeface
                (new FontFamily(_defaultFontName),
                FontStyles.Normal,
                FontWeights.Normal, FontStretches.Normal);

            FormattedText formattedText = new FormattedText(
               pieContext.Name,
               CultureInfo.CurrentUICulture,
               FlowDirection.LeftToRight,
               typeface,
               _defaultFontSize,
               Brushes.Black,
               _dpiInfo.PixelsPerDip);

            var textWidth = formattedText.Width;

            Geometry geo;
            if (pieContext.PolylineEndPoint.X > centerX)
                geo = formattedText.BuildGeometry(new Point(pieContext.PolylineEndPoint.X + 4, pieContext.PolylineEndPoint.Y - _defaultFontSize / 1.8));
            else
                geo = formattedText.BuildGeometry(new Point(pieContext.PolylineEndPoint.X - textWidth - 4, pieContext.PolylineEndPoint.Y - _defaultFontSize / 1.8));
            PathGeometry pathGeometry = geo.GetFlattenedPathGeometry();

            return pathGeometry;
        }

        /// <summary>
        /// 饼状图扇形块鼠标离开时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PiePath_MouseLeave(object sender, MouseEventArgs e)
        {
            PART_Popup.IsOpen = false;
            Path path = sender as Path;
            PieContext pieContext = path.DataContext as PieContext;

            TranslateTransform ttf = new TranslateTransform
            {
                X = 0,
                Y = 0
            };
            path.RenderTransform = ttf;
            pieContext.Line.RenderTransform = new TranslateTransform()
            {
                X = 0,
                Y = 0,
            };

            pieContext.TextPath.RenderTransform = new TranslateTransform()
            {
                X = 0,
                Y = 0,
            };

            path.Effect = new DropShadowEffect()
            {
                Color = (Color)ColorConverter.ConvertFromString("#FF949494"),
                BlurRadius = 20,
                Opacity = 0,
                ShadowDepth = 0
            };
            _flg = false;
        }

        /// <summary>
        /// 饼状图扇形块鼠标置上移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PiePath_MouseMove(object sender, MouseEventArgs e)
        {
            Path path = sender as Path;

            //动画
            if (!_flg)
            {
                BegionOffsetAnimation(path);
            }

            PieContext pieContext = path.DataContext as PieContext;

            if(pieContext.Tooltip != null && pieContext.Tooltip.Show)
            {
                Point mousePosition = e.GetPosition((UIElement)MainCanvas.Parent);
                if (!PART_Popup.IsOpen)
                {
                    PART_Popup.IsOpen = true;
                }

                StackPanel pc = new StackPanel() { Orientation = Orientation.Vertical };

                TextBlock st = new TextBlock() { Text = pieContext.SerieName, Margin = new Thickness(0, 0, 0, 3), FontWeight = FontWeights.Black };
                pc.Children.Add(st);

                StackPanel ch = new StackPanel() { Orientation = Orientation.Horizontal };
                Border r = new Border() { Width = 10, Height = 10, CornerRadius = new CornerRadius(5), VerticalAlignment = VerticalAlignment.Center, Background = pieContext.Color, Margin = new Thickness(0, 0, 5, 0) };

                string text;
                if (!string.IsNullOrEmpty(pieContext.Tooltip.Formatter))
                {
                    text = $"{pieContext.Name} : {(pieContext.Value / pieContext.Sum).ToString(pieContext.Tooltip.Formatter)}";
                }
                else
                {
                    text = $"{pieContext.Name} : {pieContext.Value}";
                }
                
                TextBlock t = new TextBlock() { Text = text, VerticalAlignment = VerticalAlignment.Center };
                ch.Children.Add(r);
                ch.Children.Add(t);
                pc.Children.Add(ch);

                Border border = new Border
                {
                    Effect = new DropShadowEffect() { BlurRadius = 15, Opacity = 0.3, ShadowDepth = 0 },
                    Margin = new Thickness(15),
                    Child = pc,
                    Background = Brushes.White,
                    Padding = new Thickness(5),
                    CornerRadius = new CornerRadius(3)
                };
                System.Windows.Controls.Grid grid = new System.Windows.Controls.Grid();
                grid.Children.Add(border);

                PART_Popup.Child = grid;
                PART_Popup.HorizontalOffset = mousePosition.X + 5;
                PART_Popup.VerticalOffset = mousePosition.Y + 5;
                PART_Popup.IsOpen = true;
            }
        }

        /// <summary>
        /// 扇形块滑出动画效果
        /// </summary>
        /// <param name="path"></param>
        private void BegionOffsetAnimation(Path path)
        {
            NameScope.SetNameScope(this, new NameScope());
            PieContext pathDataContext = path.DataContext as PieContext;
            double angle = pathDataContext.Angle;
            double sum = pathDataContext.Sum;

            Point minPoint = new Point(Math.Round(pathDataContext.StarPoint.X + pathDataContext.EndPoint.X) / 2, Math.Round(pathDataContext.StarPoint.Y + pathDataContext.EndPoint.Y) / 2);

            Vector v1 = minPoint - pathDataContext.CenterPoint;

            Vector v2 = new Point(2000, 0) - new Point(0, 0);
            double vAngle;
            if (180 < angle && angle <= 360 && pathDataContext.Value / sum >= 0.5)
            {
                vAngle = Math.Round(Vector.AngleBetween(v2, -v1));
            }
            else
            {
                vAngle = Math.Round(Vector.AngleBetween(v2, v1));
            }

            double offsetX = 10 * Math.Cos(vAngle * Math.PI / 180);
            double offsetY = 10 * Math.Sin(vAngle * Math.PI / 180);

            Polyline line3 = pathDataContext.Line;
            Path textPath = pathDataContext.TextPath;
            textPath.Cursor = Cursors.Hand;

            TranslateTransform LineAnimatedTranslateTransform =
                new TranslateTransform();
            RegisterName("LineAnimatedTranslateTransform", LineAnimatedTranslateTransform);
            line3.RenderTransform = LineAnimatedTranslateTransform;

            TranslateTransform animatedTranslateTransform =
                new TranslateTransform();
            RegisterName("AnimatedTranslateTransform", animatedTranslateTransform);
            path.RenderTransform = animatedTranslateTransform;

            TranslateTransform TextAnimatedTranslateTransform =
               new TranslateTransform();
            RegisterName("TextAnimatedTranslateTransform", animatedTranslateTransform);
            textPath.RenderTransform = animatedTranslateTransform;

            DoubleAnimation daX = new DoubleAnimation();
            Storyboard.SetTargetProperty(daX, new PropertyPath(TranslateTransform.XProperty));
            daX.Duration = new Duration(TimeSpan.FromSeconds(0.2));
            daX.From = 0;
            daX.To = offsetX;

            DoubleAnimation daY = new DoubleAnimation();

            Storyboard.SetTargetName(daY, nameof(animatedTranslateTransform));
            Storyboard.SetTargetProperty(daY, new PropertyPath(TranslateTransform.YProperty));
            daY.Duration = new Duration(TimeSpan.FromSeconds(0.2));
            daY.From = 0;
            daY.To = offsetY;

            path.Effect = new DropShadowEffect()
            {
                Color = (Color)ColorConverter.ConvertFromString("#2E2E2E"),
                BlurRadius = 33,
                Opacity = 0.6,
                ShadowDepth = 0
            };

            animatedTranslateTransform.BeginAnimation(TranslateTransform.XProperty, daX);
            animatedTranslateTransform.BeginAnimation(TranslateTransform.YProperty, daY);
            LineAnimatedTranslateTransform.BeginAnimation(TranslateTransform.XProperty, daX);
            LineAnimatedTranslateTransform.BeginAnimation(TranslateTransform.YProperty, daY);
            TextAnimatedTranslateTransform.BeginAnimation(TranslateTransform.XProperty, daX);
            TextAnimatedTranslateTransform.BeginAnimation(TranslateTransform.YProperty, daY);

            _flg = true;
        }
    }
}
